package com.example.JavaWebApi.core.mybatisplusextention.wrapperCreator;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.example.JavaWebApi.core.mybatisplusextention.wrapperCreator.annotation.ColumnTarget;
import com.example.JavaWebApi.core.mybatisplusextention.wrapperCreator.annotation.IgnoreValue;
import com.example.JavaWebApi.core.mybatisplusextention.wrapperCreator.annotation.Operator;
import com.example.JavaWebApi.core.mybatisplusextention.wrapperCreator.model.AggregateConditionModel;
import com.example.JavaWebApi.core.mybatisplusextention.wrapperCreator.model.QueryConditionModel;
import lombok.Data;
import lombok.SneakyThrows;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @program: java-web-api
 * @description: 条件构造器生成类
 * @author: 杨泽
 * @create: 2023-07-03 17:08
 */
@Data
public class QueryWrapperCreator<TQuery> {
    private List<QueryConditionModel> queryConditionModelList;
    private List<AggregateConditionModel> aggregateConditionModels = new ArrayList<>();
    private TQuery query;

    public QueryWrapperCreator(TQuery query) {
        this.query = query;
        initModel(query);
    }

    public <T> QueryWrapper<T> createWrapper() {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        createWrapper(queryWrapper, queryConditionModelList
                .stream()
                .filter(t -> !t.isAggregate())
                .sorted((a, b) -> {
                    if (a.getConnectorType() == ConnectorType.And) {
                        return 1;
                    } else {
                        return -1;
                    }
                })
                .collect(Collectors.toList()));
        createAggregateWrapper(queryWrapper);
        return queryWrapper;
    }

    /**
     * Description: 初始化查询条件模型
     *
     * @param query
     * @return void
     * @author 杨泽
     * @date 2023/7/6 10:00
     */
    @SneakyThrows
    private void initModel(TQuery query) {
        List<QueryConditionModel> result = new ArrayList<>();
        for (Field field : query.getClass().getDeclaredFields()) {
			IgnoreValue ignoreValue=field.getAnnotation(IgnoreValue.class);
            if(ignoreValue!=null){
                continue;
            }
            Operator operator = field.getAnnotation(Operator.class);
            OperatorType operatorType = OperatorType.Equal;
            ConnectorType connectorType = ConnectorType.And;
            ColumnTarget fieldTarget = field.getAnnotation(ColumnTarget.class);
            String column = null;
            if (operator != null) {
                operatorType = operator.operatorType();
                connectorType = operator.connectorType();
            }
            if (fieldTarget != null) {
                column = fieldTarget.columnName();
            } else {
                column = field.getName();
            }
            QueryConditionModel model = new QueryConditionModel();
            field.setAccessible(true);
            model.setField(field);
            model.setValue(field.get(query));
            model.setColumn(column);
            model.setConnectorType(connectorType);
            model.setOperatorType(operatorType);
            result.add(model);
        }
        queryConditionModelList = result;
    }

    /**
     * Description: 设置聚合条件
     *
     * @param connectorType 条件连接类型
     * @param conditions    聚合的条件
     * @return com.example.JavaWebApi.core.mybatisplusextention.wrapperCreator.QueryWrapperCreator
     * @author 杨泽
     * @date 2023/7/6 8:38
     */
    public QueryWrapperCreator setAggregateCondition(ConnectorType connectorType, SFunction<TQuery, ?>... conditions) {
        AggregateConditionModel aggregateConditionModel = new AggregateConditionModel();
        for (SFunction<TQuery, ?> condition : conditions) {
            String fieldName = LambdaUtil.getFieldName(condition);
            queryConditionModelList.stream()
                    .filter(t -> t.getField().getName().equals(fieldName))
                    .findFirst()
                    .ifPresent(t -> {
                        aggregateConditionModel.setOuterConnector(connectorType);
                        aggregateConditionModel.getConditionModelList().add(t);
                        t.setAggregate(true);
                    });
        }
        aggregateConditionModels.add(aggregateConditionModel);
        return this;
    }

    /**
     * Description: 设置聚合条件
     *
     * @param connectorType 条件连接类型
     * @param fields        字段名
     * @return com.example.JavaWebApi.core.mybatisplusextention.wrapperCreator.QueryWrapperCreator
     * @author 杨泽
     * @date 2023/7/6 9:01
     */
    public QueryWrapperCreator setAggregateCondition(ConnectorType connectorType, String... fields) {
        AggregateConditionModel aggregateConditionModel = new AggregateConditionModel();
        for (String field : fields) {
            queryConditionModelList.stream()
                    .filter(t -> t.getField().getName().equals(field))
                    .findFirst()
                    .ifPresent(t -> {
                        aggregateConditionModel.setOuterConnector(connectorType);
                        aggregateConditionModel.getConditionModelList().add(t);
                        t.setAggregate(true);
                    });
        }
        aggregateConditionModels.add(aggregateConditionModel);
        return this;
    }

    /**
     * Description: 构造聚合条件的wrapper
     *
     * @param queryWrapper
     * @return void
     * @author 杨泽
     * @date 2023/7/6 10:01
     */
    private <T> void createAggregateWrapper(QueryWrapper<T> queryWrapper) {
        for (AggregateConditionModel aggregateConditionModel : aggregateConditionModels.stream().sorted((a, b) -> {
            if (a.getOuterConnector() == ConnectorType.And) {
                return 1;
            } else {
                return -1;
            }
        }).collect(Collectors.toList())) {
            if (aggregateConditionModel.canBuild()) {
                if (aggregateConditionModel.getOuterConnector() == ConnectorType.And) {
                    queryWrapper.and(wrapper -> {
                        createWrapper(wrapper, aggregateConditionModel.getConditionModelList());
                    });
                } else {
                    queryWrapper.or(wrapper -> {
                        createWrapper(wrapper, aggregateConditionModel.getConditionModelList());
                    });
                }
            }
        }
    }

    /**
     * Description: 通用wrapper构造方法
     *
     * @param queryWrapper
     * @param queryConditionModels
     * @return void
     * @author 杨泽
     * @date 2023/7/6 10:01
     */
    private <T> void createWrapper(QueryWrapper<T> queryWrapper, List<QueryConditionModel> queryConditionModels) {
        for (QueryConditionModel queryConditionModel : queryConditionModels) {
            if (queryConditionModel.getValue() != null) {
                if (queryConditionModel.getValue() instanceof String) {
                    String strValue = (String) queryConditionModel.getValue();
                    if (StringUtils.isNotBlank(strValue)) {
                        setConnector(queryWrapper, queryConditionModel);
                    }
                } else {
                    setConnector(queryWrapper, queryConditionModel);
                }
            }
        }
    }

    private <T> void setConnector(QueryWrapper<T> queryWrapper, QueryConditionModel queryConditionModel) {
        switch (queryConditionModel.getConnectorType()) {
            case Or:
                queryWrapper.or(wrapper -> setOperator(wrapper, queryConditionModel));
                break;
            default:
                queryWrapper.and(wrapper -> setOperator(wrapper, queryConditionModel));
                break;
        }
    }


    private <T> void setOperator(QueryWrapper<T> queryWrapper, QueryConditionModel queryConditionModel) {
        String column = queryConditionModel.getColumn();
        Object value = queryConditionModel.getValue();
        switch (queryConditionModel.getOperatorType()) {
            case NotEqual:
                queryWrapper.ne(column, value);
                break;
            case Like:
                queryWrapper.like(column, value);
                break;
            case NotLike:
                queryWrapper.notLike(column, value);
                break;
            case GreaterThan:
                queryWrapper.gt(column, value);
                break;
            case GreaterThanOrEqual:
                queryWrapper.ge(column, value);
                break;
            case LessThan:
                queryWrapper.lt(column, value);
                break;
            case LessThanOrEqual:
                queryWrapper.le(column, value);
                break;
            case LikeLeft:
                queryWrapper.likeLeft(column, value);
                break;
            case LikeRight:
                queryWrapper.likeRight(column, value);
                break;
            case In:
                if (value instanceof String) {
                    String[] arr = ((String) value).split(",");
                    queryWrapper.in(column, arr);
                } else {
                    queryWrapper.in(column, value);
                }
                break;
            case NotIn:
                if (value instanceof String) {
                    String[] arr = ((String) value).split(",");
                    queryWrapper.notIn(column, arr);
                } else {
                    queryWrapper.notIn(column, value);
                }
                break;
            default:
                queryWrapper.eq(column, value);
                break;
        }
    }
}
